-
Notifications
You must be signed in to change notification settings - Fork 9
feat/meetup-presentations #484
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
|
Important Review skippedDraft detected. Please check the settings in the CodeRabbit UI or the You can disable this status message by setting the Note Other AI code review bot(s) detectedCodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review. WalkthroughThis PR adds a presentations feature: contracts, data-access service and NgRx-signal stores, list and detail feature pages, presentation card and ebook UI components, i18n translations, routing, and build/test/lint configurations across multiple new libs and project entries. Changes
Sequence Diagram(s)sequenceDiagram
actor User
participant Page as PresentationsPage
participant Store as PresentationsListStore
participant Service as PresentationsService
participant Data as Mock/API
User->>Page: Navigate /presentations
Page->>Store: fetchPresentationsList(query)
Store->>Store: set LOADING
Store->>Service: getPresentationsList(query)
Service->>Data: request (mock)
Data-->>Service: array response
Service-->>Store: observable emits
Store->>Store: patch presentations, total, set LOADED
Page->>User: render cards + pagination
sequenceDiagram
actor User
participant Page as PresentationDetailsPage
participant Store as PresentationDetailStore
participant Service as PresentationsService
participant Data as Mock/API
User->>Page: Navigate /presentation/:slug
Page->>Store: fetchPresentationDetail(slug)
Store->>Store: set LOADING
Store->>Service: getPresentationDetail(slug)
Service->>Data: find by slug
alt found
Data-->>Service: presentation
Service-->>Store: observable emits
Store->>Store: patch presentation, set LOADED
Page->>User: render detail + ebook + workshops
else not found
Data-->>Service: error
Service-->>Store: error thrown
Store->>Store: patch error state
Page->>User: render error fallback
end
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~45 minutes Possibly related PRs
Suggested reviewers
Poem
Pre-merge checks and finishing touches✅ Passed checks (3 passed)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
|
PR is detected, will deploy to dev environment |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull Request Overview
This PR adds a new presentations/talks feature to the blog platform, enabling users to browse and view meetup presentations with dedicated list and detail pages.
Key Changes:
- Created new presentation domain with contracts, data access layer, and UI components
- Added routing for presentations list (
/presentations) and detail pages (/presentation/:slug) - Introduced reusable ebook component for promoting downloadable content
Reviewed Changes
Copilot reviewed 80 out of 91 changed files in this pull request and generated 5 comments.
Show a summary per file
| File | Description |
|---|---|
| tsconfig.base.json | Added path mappings for new presentations libraries and shared ebook component |
| libs/blog/shared/ui-icon/src/lib/icon/icon.component.ts | Added 'video' icon type for presentation cards |
| libs/blog/shared/ui-ebook/* | New reusable ebook promotion component with translations |
| libs/blog/presentations/ui-presentation-card/* | New presentation card component with skeleton loader |
| libs/blog/presentations/presentations-page/* | Presentations list page with pagination |
| libs/blog/presentations/presentation-details-page/* | Individual presentation detail page |
| libs/blog/presentations/data-access/* | State management and service layer with mock data |
| libs/blog-contracts/presentations/* | TypeScript contracts defining presentation data structure |
| libs/blog/articles/feature-shell/src/lib/routes.ts | Added presentation routes to routing configuration |
| libs/blog/articles/feature-list/src/lib/category-section-container/* | Fixed typo: skeleonLoaders → skeletonLoaders |
| apps/blog/src/assets/i18n/*.json | Added translations for presentations and ebook sections |
Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.
| "executor": "@nx/jest:jest", | ||
| "outputs": ["{workspaceRoot}/coverage/{projectRoot}"], | ||
| "options": { | ||
| "jestConfig": "libs/blog/videos/ui-video-card/jest.config.ts" |
Copilot
AI
Oct 22, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The jestConfig path references 'videos/ui-video-card' but should reference 'presentations/ui-presentation-card' to match this library's actual location.
| "jestConfig": "libs/blog/videos/ui-video-card/jest.config.ts" | |
| "jestConfig": "libs/blog/presentations/ui-presentation-card/jest.config.ts" |
| displayName: 'blog-videos-ui-video-card', | ||
| preset: '../../../../jest.preset.js', | ||
| setupFilesAfterEnv: ['<rootDir>/src/test-setup.ts'], | ||
| coverageDirectory: '../../../../coverage/libs/blog/videos/ui-video-card', |
Copilot
AI
Oct 22, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The displayName and coverageDirectory reference 'videos/ui-video-card' but should be 'presentations/ui-presentation-card' to match this library's actual name and location.
| displayName: 'blog-videos-ui-video-card', | |
| preset: '../../../../jest.preset.js', | |
| setupFilesAfterEnv: ['<rootDir>/src/test-setup.ts'], | |
| coverageDirectory: '../../../../coverage/libs/blog/videos/ui-video-card', | |
| displayName: 'blog-presentations-ui-presentation-card', | |
| preset: '../../../../jest.preset.js', | |
| setupFilesAfterEnv: ['<rootDir>/src/test-setup.ts'], | |
| coverageDirectory: '../../../../coverage/libs/blog/presentations/ui-presentation-card', |
| # blog-videos-ui-video-card | ||
|
|
||
| This library was generated with [Nx](https://nx.dev). | ||
|
|
||
| ## Running unit tests | ||
|
|
||
| Run `nx test blog-videos-ui-video-card` to execute the unit tests. |
Copilot
AI
Oct 22, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The library name should be 'blog-presentations-ui-presentation-card' instead of 'blog-videos-ui-video-card' to match the actual library being created.
| # blog-videos-ui-video-card | |
| This library was generated with [Nx](https://nx.dev). | |
| ## Running unit tests | |
| Run `nx test blog-videos-ui-video-card` to execute the unit tests. | |
| # blog-presentations-ui-presentation-card | |
| This library was generated with [Nx](https://nx.dev). | |
| ## Running unit tests | |
| Run `nx test blog-presentations-ui-presentation-card` to execute the unit tests. |
| # blog-videos-ui-video-card | ||
|
|
||
| This library was generated with [Nx](https://nx.dev). | ||
|
|
||
| ## Running unit tests | ||
|
|
||
| Run `nx test blog-videos-ui-video-card` to execute the unit tests. |
Copilot
AI
Oct 22, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The library name should be 'blog-presentations-ui-presentation-card' instead of 'blog-videos-ui-video-card' to match the actual library being created.
| # blog-videos-ui-video-card | |
| This library was generated with [Nx](https://nx.dev). | |
| ## Running unit tests | |
| Run `nx test blog-videos-ui-video-card` to execute the unit tests. | |
| # blog-presentations-ui-presentation-card | |
| This library was generated with [Nx](https://nx.dev). | |
| ## Running unit tests | |
| Run `nx test blog-presentations-ui-presentation-card` to execute the unit tests. |
apps/blog/src/assets/i18n/pl.json
Outdated
| }, | ||
| "presentations": { | ||
| "listTitle": "Prezentacje Meetup", | ||
| "watchRecording": "OBEJRZYJ NAGRYWANIE", |
Copilot
AI
Oct 22, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Corrected spelling of 'NAGRYWANIE' - should be 'NAGRANIE' (recording as a noun, not the gerund form).
| "watchRecording": "OBEJRZYJ NAGRYWANIE", | |
| "watchRecording": "OBEJRZYJ NAGRANIE", |
|
Deployed to dev environment |
|
PR is detected, will deploy to dev environment |
|
Deployed to dev environment |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 15
🧹 Nitpick comments (8)
libs/blog/presentations/presentations-page/src/lib/presentations-page/presentations-page.component.spec.ts (1)
1-22: Consider expanding test coverage beyond creation check.The test successfully verifies component instantiation, which is a good starting point. As the component evolves, consider adding tests for:
- Component interactions with the presentations store
- Pagination behavior
- Loading states and skeleton rendering
- User interactions
libs/blog-contracts/presentations/.eslintrc.json (1)
4-17: Remove redundant override blocks.The three override blocks all define empty rules. The first override already covers all file types (
*.ts,*.tsx,*.js,*.jsx), making the subsequent two blocks redundant.Apply this diff to simplify the configuration:
"overrides": [ { "files": ["*.ts", "*.tsx", "*.js", "*.jsx"], "rules": {} - }, - { - "files": ["*.ts", "*.tsx"], - "rules": {} - }, - { - "files": ["*.js", "*.jsx"], - "rules": {} } ]libs/blog/shared/ui-ebook/src/lib/ui-ebook/ui-ebook.component.spec.ts (1)
5-22: Consider expanding test coverage.The current test only verifies component creation. While this smoke test is valuable, consider adding tests for:
- Translation key bindings render correctly
- Image asset is present with correct attributes
- Download button is rendered and accessible
- Card structure and directives are applied
Example additional tests:
it('should render ebook image with correct attributes', () => { const img = fixture.nativeElement.querySelector('img'); expect(img).toBeTruthy(); expect(img.getAttribute('ngSrc')).toBe('assets/al-ebook.png'); expect(img.getAttribute('priority')).toBe(''); }); it('should render download button', () => { const button = fixture.nativeElement.querySelector('[al-button]'); expect(button).toBeTruthy(); expect(button.getAttribute('role')).toBe('link'); });libs/blog/presentations/ui-presentation-card/src/lib/components/presentation-card/presentation-card.component.html (1)
33-39: Consider moving inline style to CSS class.The inline
style="word-break: break-word"could be moved to a Tailwind utility class or component styles for better maintainability.Apply this diff:
<h3 class="*:text-al-primary-foreground text-2xl font-bold *:not-italic" [id]="presentation().slug" - style="word-break: break-word" + class="*:text-al-primary-foreground text-2xl font-bold *:not-italic break-words" >Note: Tailwind's
break-wordsutility appliesword-break: break-word.libs/blog/articles/feature-shell/src/lib/routes.ts (1)
22-27: Add SEO/data for new routes (consistency with existing article routes).Other routes provide
data.seo,title,id. Mirror that here.{ path: 'presentations', loadComponent: async () => (await import('@angular-love/blog/presentations/presentations-page')) .PresentationsPageComponent, + data: { + seo: { title: 'Presentations', autoHrefLang: true }, + title: 'Presentations', + id: 'presentations', + }, }, { path: 'presentation/:presentationSlug', loadComponent: async () => ( await import( '@angular-love/blog/presentations/presentation-details-page' ) ).PresentationDetailsPageComponent, + data: { + seo: { title: 'Presentation', autoHrefLang: true }, + title: 'Presentation', + id: 'presentation-details', + }, },Also applies to: 28-36
libs/blog/presentations/data-access/src/lib/state/presentations-list.store.ts (1)
36-61: PreferswitchMapto cancel stale requests during rapid pagination.Improves UX and reduces wasted work. Optional, keeps semantics consistent with typical list fetch flows.
-import { concatMap, pipe, tap } from 'rxjs'; +import { switchMap, pipe, tap } from 'rxjs'; @@ - concatMap((query) => + switchMap((query) => presentationsService.getPresentationsList({ ...query }).pipe(libs/blog/presentations/data-access/src/lib/infrastructure/presentations.service.ts (1)
109-121: Consider using the query parameter in the mock implementation.While this is mock data, respecting the
takeparameter would make pagination testing more realistic and align mock behavior with future API expectations.Apply this diff to enhance the mock:
getPresentationsList( query: PresentationsQuery, ): Observable<ArrayResponse<PresentationPreview>> { + const take = query?.take ?? MOCK_PRESENTATIONS.length; + const data = MOCK_PRESENTATIONS.slice(0, take); return of({ - data: MOCK_PRESENTATIONS, + data, total: MOCK_PRESENTATIONS.length, }); // TODO - add real APIlibs/blog/presentations/presentation-details-page/src/lib/presentation-details-page/presentation-details-page.component.ts (1)
42-46: Simplify by removing the unnecessarycomputedwrapper.The
computed(() => this.presentationSlug())wrapper is redundant sincerxMethodaccepts signals directly andthis.presentationSlugis already aSignal<string>. The similarArticleDetailsContainerComponentpattern shows passing the signal directly.Apply this diff to simplify:
constructor() { this.presentationDetailStore.fetchPresentationDetail( - computed(() => this.presentationSlug()), + this.presentationSlug, ); }
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (3)
apps/blog/src/assets/al-ebook.pngis excluded by!**/*.pngapps/blog/src/assets/icons/video-content.svgis excluded by!**/*.svgapps/blog/src/assets/icons/video.svgis excluded by!**/*.svg
📒 Files selected for processing (80)
apps/blog/src/assets/i18n/en.json(2 hunks)apps/blog/src/assets/i18n/pl.json(2 hunks)libs/blog-contracts/presentations/.eslintrc.json(1 hunks)libs/blog-contracts/presentations/README.md(1 hunks)libs/blog-contracts/presentations/jest.config.ts(1 hunks)libs/blog-contracts/presentations/project.json(1 hunks)libs/blog-contracts/presentations/src/index.ts(1 hunks)libs/blog-contracts/presentations/src/lib/presentations.ts(1 hunks)libs/blog-contracts/presentations/tsconfig.json(1 hunks)libs/blog-contracts/presentations/tsconfig.lib.json(1 hunks)libs/blog-contracts/presentations/tsconfig.spec.json(1 hunks)libs/blog/articles/feature-list/src/lib/category-section-container/category-section-container.component.html(2 hunks)libs/blog/articles/feature-list/src/lib/category-section-container/category-section-container.component.ts(1 hunks)libs/blog/articles/feature-shell/src/lib/routes.ts(1 hunks)libs/blog/home/feature-home/src/lib/home-page/home-page.component.html(1 hunks)libs/blog/layouts/ui-layouts/src/lib/layout/layout.component.html(1 hunks)libs/blog/presentations/data-access/.eslintrc.json(1 hunks)libs/blog/presentations/data-access/README.md(1 hunks)libs/blog/presentations/data-access/jest.config.ts(1 hunks)libs/blog/presentations/data-access/project.json(1 hunks)libs/blog/presentations/data-access/src/index.ts(1 hunks)libs/blog/presentations/data-access/src/lib/dto/presentations.query.ts(1 hunks)libs/blog/presentations/data-access/src/lib/infrastructure/presentations.service.ts(1 hunks)libs/blog/presentations/data-access/src/lib/state/presentation-detail.store.ts(1 hunks)libs/blog/presentations/data-access/src/lib/state/presentations-list.store.ts(1 hunks)libs/blog/presentations/data-access/src/test-setup.ts(1 hunks)libs/blog/presentations/data-access/tsconfig.json(1 hunks)libs/blog/presentations/data-access/tsconfig.lib.json(1 hunks)libs/blog/presentations/data-access/tsconfig.spec.json(1 hunks)libs/blog/presentations/presentation-details-page/.eslintrc.json(1 hunks)libs/blog/presentations/presentation-details-page/README.md(1 hunks)libs/blog/presentations/presentation-details-page/jest.config.ts(1 hunks)libs/blog/presentations/presentation-details-page/project.json(1 hunks)libs/blog/presentations/presentation-details-page/src/index.ts(1 hunks)libs/blog/presentations/presentation-details-page/src/lib/presentation-details-page/presentation-details-page.component.html(1 hunks)libs/blog/presentations/presentation-details-page/src/lib/presentation-details-page/presentation-details-page.component.ts(1 hunks)libs/blog/presentations/presentation-details-page/src/test-setup.ts(1 hunks)libs/blog/presentations/presentation-details-page/tsconfig.json(1 hunks)libs/blog/presentations/presentation-details-page/tsconfig.lib.json(1 hunks)libs/blog/presentations/presentation-details-page/tsconfig.spec.json(1 hunks)libs/blog/presentations/presentations-page/.eslintrc.json(1 hunks)libs/blog/presentations/presentations-page/README.md(1 hunks)libs/blog/presentations/presentations-page/jest.config.ts(1 hunks)libs/blog/presentations/presentations-page/project.json(1 hunks)libs/blog/presentations/presentations-page/src/index.ts(1 hunks)libs/blog/presentations/presentations-page/src/lib/presentations-page/presentations-page.component.html(1 hunks)libs/blog/presentations/presentations-page/src/lib/presentations-page/presentations-page.component.spec.ts(1 hunks)libs/blog/presentations/presentations-page/src/lib/presentations-page/presentations-page.component.ts(1 hunks)libs/blog/presentations/presentations-page/src/test-setup.ts(1 hunks)libs/blog/presentations/presentations-page/tsconfig.json(1 hunks)libs/blog/presentations/presentations-page/tsconfig.lib.json(1 hunks)libs/blog/presentations/presentations-page/tsconfig.spec.json(1 hunks)libs/blog/presentations/ui-presentation-card/.eslintrc.json(1 hunks)libs/blog/presentations/ui-presentation-card/README.md(1 hunks)libs/blog/presentations/ui-presentation-card/jest.config.ts(1 hunks)libs/blog/presentations/ui-presentation-card/project.json(1 hunks)libs/blog/presentations/ui-presentation-card/src/index.ts(1 hunks)libs/blog/presentations/ui-presentation-card/src/lib/components/presentation-card/presentation-card-skeleton.component.ts(1 hunks)libs/blog/presentations/ui-presentation-card/src/lib/components/presentation-card/presentation-card.component.html(1 hunks)libs/blog/presentations/ui-presentation-card/src/lib/components/presentation-card/presentation-card.component.ts(1 hunks)libs/blog/presentations/ui-presentation-card/src/lib/components/presentation-card/presentation-card.stories.ts(1 hunks)libs/blog/presentations/ui-presentation-card/src/test-setup.ts(1 hunks)libs/blog/presentations/ui-presentation-card/tsconfig.json(1 hunks)libs/blog/presentations/ui-presentation-card/tsconfig.lib.json(1 hunks)libs/blog/presentations/ui-presentation-card/tsconfig.spec.json(1 hunks)libs/blog/shared/ui-ebook/.eslintrc.json(1 hunks)libs/blog/shared/ui-ebook/README.md(1 hunks)libs/blog/shared/ui-ebook/jest.config.ts(1 hunks)libs/blog/shared/ui-ebook/project.json(1 hunks)libs/blog/shared/ui-ebook/src/index.ts(1 hunks)libs/blog/shared/ui-ebook/src/lib/ui-ebook/ui-ebook.component.html(1 hunks)libs/blog/shared/ui-ebook/src/lib/ui-ebook/ui-ebook.component.scss(1 hunks)libs/blog/shared/ui-ebook/src/lib/ui-ebook/ui-ebook.component.spec.ts(1 hunks)libs/blog/shared/ui-ebook/src/lib/ui-ebook/ui-ebook.component.ts(1 hunks)libs/blog/shared/ui-ebook/src/test-setup.ts(1 hunks)libs/blog/shared/ui-ebook/tsconfig.json(1 hunks)libs/blog/shared/ui-ebook/tsconfig.lib.json(1 hunks)libs/blog/shared/ui-ebook/tsconfig.spec.json(1 hunks)libs/blog/shared/ui-icon/src/lib/icon/icon.component.ts(1 hunks)tsconfig.base.json(4 hunks)
🧰 Additional context used
🧬 Code graph analysis (17)
libs/blog/presentations/data-access/src/lib/dto/presentations.query.ts (1)
libs/blog/authors/data-access/src/lib/state/author-details.store.ts (1)
AuthorArticlesQuery(23-26)
libs/blog/presentations/presentations-page/src/index.ts (1)
libs/blog/shell/feature-shell-web/src/lib/blog-shell.routes.ts (3)
HomePageComponent(30-32)NewsletterPageComponent(74-76)m(91-91)
libs/blog/presentations/data-access/src/lib/state/presentations-list.store.ts (4)
libs/blog-contracts/presentations/src/lib/presentations.ts (1)
PresentationPreview(3-9)libs/blog/presentations/data-access/src/lib/dto/presentations.query.ts (1)
PresentationsQuery(1-3)libs/blog/i18n/data-access/src/lib/state/with-lang.state.ts (1)
withLangState(6-13)libs/shared/utils-signal-store/src/lib/with-callstate.function.ts (1)
withCallState(58-113)
libs/blog/shared/ui-ebook/src/lib/ui-ebook/ui-ebook.component.ts (3)
libs/blog/presentations/presentation-details-page/src/lib/presentation-details-page/presentation-details-page.component.ts (1)
Component(16-47)libs/blog/presentations/presentations-page/src/lib/presentations-page/presentations-page.component.ts (1)
Component(26-65)libs/blog/shared/ui-card/src/lib/card.component.ts (1)
CardComponent(97-104)
libs/blog/presentations/ui-presentation-card/src/index.ts (1)
libs/blog/shared/ui-card/src/lib/card-skeleton.component.ts (1)
CardSkeletonComponent(34-39)
libs/blog/presentations/ui-presentation-card/src/lib/components/presentation-card/presentation-card.stories.ts (1)
libs/blog-contracts/presentations/src/lib/presentations.ts (1)
PresentationPreview(3-9)
libs/blog/layouts/ui-layouts/src/lib/layout/layout.component.html (1)
libs/blog/layouts/ui-layouts/src/lib/layout/layout.component.ts (1)
LayoutComponent(23-40)
libs/blog-contracts/presentations/src/lib/presentations.ts (1)
libs/blog-contracts/articles/src/lib/articles.ts (2)
ArticlePreview(24-37)Article(106-132)
libs/blog/presentations/presentations-page/src/lib/presentations-page/presentations-page.component.html (2)
libs/blog/shared/ui-pagination/src/lib/pagination/pagination.component.ts (1)
PaginationComponent(33-75)libs/blog/search/feature-search-results-page/src/lib/search-results-page/search-results-page.presenter.ts (1)
SearchResultsPagePresenter(18-38)
libs/blog/articles/feature-shell/src/lib/routes.ts (1)
libs/blog/shell/feature-shell-web/src/lib/blog-shell.routes.ts (4)
FeatureAboutUsComponent(47-49)FeatureAuthorComponent(56-58)HomePageComponent(30-32)NewsletterPageComponent(74-76)
libs/blog/presentations/presentations-page/src/lib/presentations-page/presentations-page.component.ts (3)
libs/blog/presentations/data-access/src/lib/state/presentations-list.store.ts (1)
PresentationsListStore(28-64)libs/blog/shared/ui-pagination/src/lib/pagination/pagination.component.ts (2)
PageChangeEvent(17-20)PaginationComponent(33-75)libs/blog/articles/feature-category/src/lib/category-articles.component.ts (1)
CategoryArticlesComponent(41-69)
libs/blog/presentations/data-access/src/lib/infrastructure/presentations.service.ts (4)
libs/blog/presentations/data-access/src/lib/dto/presentations.query.ts (1)
PresentationsQuery(1-3)libs/blog-contracts/shared/src/lib/shared/array-response.model.ts (1)
ArrayResponse(1-4)libs/blog-contracts/presentations/src/lib/presentations.ts (1)
PresentationPreview(3-9)libs/blog/articles/data-access/src/lib/infrastructure/articles.service.ts (3)
ArticlesService(12-44)getArticlePreviewBySlug(20-26)getArticleList(28-37)
libs/blog/presentations/data-access/src/lib/state/presentation-detail.store.ts (3)
libs/blog-contracts/presentations/src/lib/presentations.ts (1)
PresentationPreview(3-9)libs/blog/i18n/data-access/src/lib/state/with-lang.state.ts (1)
withLangState(6-13)libs/shared/utils-signal-store/src/lib/with-callstate.function.ts (1)
withCallState(58-113)
libs/blog/presentations/presentation-details-page/src/lib/presentation-details-page/presentation-details-page.component.ts (2)
libs/blog/presentations/data-access/src/lib/state/presentation-detail.store.ts (1)
PresentationDetailStore(25-60)libs/blog/articles/feature-article/src/lib/article-details-container/article-details-container.component.ts (1)
ArticleDetailsContainerComponent(23-39)
libs/blog/shared/ui-ebook/src/lib/ui-ebook/ui-ebook.component.html (2)
libs/blog/shared/ui-card/src/lib/card.component.ts (2)
CardComponent(97-104)DarkCardDirective(83-88)libs/blog/shared/ui-card/src/lib/card.stories.ts (1)
template(59-67)
libs/blog/presentations/ui-presentation-card/src/lib/components/presentation-card/presentation-card-skeleton.component.ts (2)
libs/blog/presentations/presentations-page/src/lib/presentations-page/presentations-page.component.ts (1)
Component(26-65)libs/blog/presentations/ui-presentation-card/src/lib/components/presentation-card/presentation-card.component.ts (1)
Component(10-24)
libs/blog/presentations/ui-presentation-card/src/lib/components/presentation-card/presentation-card.component.ts (2)
libs/blog/presentations/presentations-page/src/lib/presentations-page/presentations-page.component.ts (1)
Component(26-65)libs/blog-contracts/presentations/src/lib/presentations.ts (1)
PresentationPreview(3-9)
🔇 Additional comments (70)
libs/blog/home/feature-home/src/lib/home-page/home-page.component.html (1)
5-5: Verify the intent of removing spacing and display classes.The removal of
mb-4 blockCSS classes changes the layout styling of the articles list. Please confirm that this styling change is intentional as part of the presentations feature refresh and that it doesn't cause any visual regressions or layout misalignment in the home page.libs/blog/shared/ui-icon/src/lib/icon/icon.component.ts (1)
23-24: LGTM! The 'video' icon type addition is ready.The SVG asset exists at
apps/blog/src/assets/icons/video.svg, so the dynamic path resolution in the component will work correctly.libs/blog/presentations/presentations-page/README.md (1)
1-7: LGTM!Standard Nx-generated documentation with appropriate test command reference.
libs/blog/shared/ui-ebook/src/lib/ui-ebook/ui-ebook.component.scss (1)
1-14: LGTM!The grid layout with responsive two-column design is well-structured. The 40ch max-width on the description enhances readability by maintaining an optimal line length.
libs/blog-contracts/presentations/README.md (1)
1-7: LGTM!Standard Nx-generated documentation with appropriate test command.
libs/blog/presentations/presentations-page/src/test-setup.ts (1)
1-6: LGTM!Strict test environment configuration is excellent practice. Enabling
errorOnUnknownElementsanderrorOnUnknownPropertieshelps catch template errors early and aligns with similar setups throughout the codebase.libs/blog/articles/feature-list/src/lib/category-section-container/category-section-container.component.ts (1)
53-53: LGTM! Typo fix improves code quality.Correcting
skeleonLoaderstoskeletonLoaderseliminates the spelling error.libs/blog/presentations/data-access/tsconfig.spec.json (1)
1-16: LGTM!The test configuration is properly structured with correct paths and includes all necessary test files.
libs/blog/presentations/presentations-page/tsconfig.lib.json (1)
1-17: LGTM!The library TypeScript configuration is properly structured with correct declaration settings and appropriate test file exclusions.
libs/blog-contracts/presentations/tsconfig.lib.json (1)
1-11: LGTM!The library TypeScript configuration is properly structured with correct paths and appropriate exclusions. The inclusion of
nodetypes is reasonable for a contracts library that may define types used in Node.js contexts.libs/blog/layouts/ui-layouts/src/lib/layout/layout.component.html (1)
2-2: LGTM! Positioning context added for child components.Adding
relativepositioning is appropriate for providing a positioning context for the new presentation and ebook components that may use absolute or fixed positioning.libs/blog/presentations/presentation-details-page/tsconfig.lib.json (1)
1-17: LGTM! Standard library build configuration.This TypeScript configuration correctly enables declaration generation, excludes test files, and follows the established pattern used across other presentation libraries in this PR.
libs/blog/presentations/presentation-details-page/src/test-setup.ts (1)
1-6: LGTM! Proper Jest + Angular test environment setup.The configuration correctly uses
jest-preset-angularwith strict error checking enabled, which will help catch template issues early during testing.libs/blog-contracts/presentations/tsconfig.spec.json (1)
1-14: LGTM! Standard test configuration.This TypeScript test configuration correctly includes Jest types, test files, and uses CommonJS module format, which is appropriate for the Jest environment.
libs/blog/presentations/data-access/tsconfig.lib.json (1)
1-17: LGTM! Consistent library build configuration.This configuration follows the same pattern as other presentation libraries, correctly setting up declaration generation and excluding test files.
libs/blog/presentations/ui-presentation-card/tsconfig.lib.json (1)
1-17: LGTM! Standard library configuration.This TypeScript configuration maintains consistency with other presentation libraries in the PR, using the same structure for declaration generation and file inclusion/exclusion.
libs/blog/presentations/presentation-details-page/tsconfig.json (1)
1-28: LGTM! Properly configured Angular library.This configuration correctly:
- Extends the base TypeScript config
- Enables strict compiler options (ES2022, strict mode, etc.)
- Configures strict Angular compiler options
- Uses project references to delegate to lib and spec configurations
libs/blog/shared/ui-ebook/.eslintrc.json (1)
1-36: LGTM! Standard Angular ESLint configuration.This configuration correctly:
- Extends the root ESLint config
- Applies Nx Angular plugins for TypeScript and HTML files
- Enforces consistent "al" prefix for directives and components
- Follows Angular naming conventions (camelCase for directives, kebab-case for components)
libs/blog/presentations/data-access/.eslintrc.json (1)
1-36: LGTM!The ESLint configuration follows Angular best practices with proper selector conventions and extends the root configuration appropriately.
libs/blog/presentations/data-access/tsconfig.json (1)
1-28: LGTM!The TypeScript configuration is well-structured with strict mode enabled and proper Angular compiler options. The configuration follows best practices for Angular libraries.
libs/blog/presentations/ui-presentation-card/.eslintrc.json (1)
1-36: LGTM!The ESLint configuration is consistent with other presentation libraries and follows Angular conventions.
libs/blog/presentations/ui-presentation-card/src/test-setup.ts (1)
1-9: LGTM!The test setup correctly configures strict Angular testing environment with proper error handling for unknown elements and properties. The
@ts-expect-errordirective is appropriately documented.apps/blog/src/assets/i18n/pl.json (1)
10-11: LGTM!The Polish translations for the presentations feature and ebook promotion are well-structured and grammatically correct. The additions appropriately support the new UI components.
Also applies to: 321-334
apps/blog/src/assets/i18n/en.json (2)
10-11: LGTM!The new translation keys for Angular talks and video player are well-integrated into the existing
homePage.welcomeMessagestructure.
318-331: LGTM!The new translation sections for presentations and ebook are well-structured with descriptive keys and professional copy.
libs/blog/presentations/presentations-page/src/lib/presentations-page/presentations-page.component.html (4)
1-7: LGTM!The page title is properly configured with accessibility attributes and i18n support.
9-27: LGTM!The presentations grid is well-implemented with proper accessibility attributes, responsive layout, and efficient loading state handling using skeleton placeholders.
28-28: LGTM!The ebook component is cleanly integrated into the page.
30-38: LGTM!The pagination footer is correctly configured with all required bindings matching the PaginationComponent's interface.
libs/blog/presentations/presentations-page/src/index.ts (1)
1-1: LGTM!The barrel export follows the established pattern in the codebase.
libs/blog/presentations/data-access/src/lib/dto/presentations.query.ts (1)
1-3: I need to find and examine the presentations page component to verify how it uses pagination.Verify component implementation and backend requirements for pagination.
The review concern cannot be definitively confirmed without examining the presentations page component that reportedly uses
pagination().skipandpagination().take. The currentPresentationsQuerytype only definestake, and the service uses mock data with a TODO comment for the real API integration.Before implementing changes:
- Locate and review the presentations page component to confirm how it actually calls the store
- Verify whether
skipis computed locally or if the backend API requires both parameters- If the API uses skip-based pagination, add
skip?: numbertoPresentationsQuery; if page-based, compute skip from page number in the service layerlibs/blog/articles/feature-list/src/lib/category-section-container/category-section-container.component.html (2)
8-8: LGTM: Trailing comma added for consistency.The trailing comma after
category()maintains proper object syntax and aligns with formatting best practices.
25-25: Good catch: Fixed typo in method name.The correction from
skeleonLoaders()toskeletonLoaders()fixes a critical typo that would have caused a runtime error.libs/blog/presentations/presentation-details-page/README.md (1)
1-7: LGTM: Standard Nx library documentation.The README follows the standard Nx-generated template and correctly documents the test command for this library.
libs/blog/shared/ui-ebook/src/test-setup.ts (1)
1-6: LGTM: Proper test environment setup with strict error checking.The configuration uses
setupZoneTestEnvwith strict error detection enabled, which will help catch issues with unknown elements and properties during testing.libs/blog/shared/ui-ebook/src/index.ts (1)
1-1: LGTM: Clean barrel export.The export properly exposes the ebook UI component for consumption by other modules.
libs/blog/presentations/data-access/src/index.ts (1)
1-2: LGTM: Well-organized store exports.The barrel properly exposes both the list and detail stores, maintaining a clean separation of concerns for the presentations data access layer.
libs/blog-contracts/presentations/project.json (1)
1-19: LGTM: Well-structured Nx project configuration.The project configuration properly defines the library with appropriate tags (
scope:shared,type:contract) and standard lint/test targets.libs/blog-contracts/presentations/src/index.ts (1)
1-1: LGTM: Standard barrel export.The export properly exposes the presentations contracts for consumption across the application.
libs/blog/presentations/ui-presentation-card/src/index.ts (1)
1-2: LGTM!Clean barrel exports following the standard pattern for public API surface.
libs/blog/shared/ui-ebook/tsconfig.spec.json (1)
1-16: LGTM!Standard Jest test configuration with appropriate compiler options and file includes.
libs/blog/presentations/ui-presentation-card/tsconfig.spec.json (1)
1-16: LGTM!Standard Jest test configuration consistent with other library test configs in the project.
libs/blog/shared/ui-ebook/tsconfig.lib.json (1)
1-17: LGTM!Standard library TypeScript configuration with appropriate declaration settings and test file exclusions.
libs/blog/presentations/presentation-details-page/src/index.ts (1)
1-1: LGTM!Clean barrel export following the standard pattern.
libs/blog/presentations/presentations-page/project.json (1)
1-20: LGTM!Standard Nx project configuration with appropriate library setup, test and lint targets correctly defined.
libs/blog/presentations/ui-presentation-card/tsconfig.json (1)
1-28: LGTM!Excellent TypeScript configuration with strict mode and modern target (es2022). The empty
filesandincludearrays are intentional and correct when using project references.libs/blog/presentations/presentations-page/tsconfig.spec.json (1)
1-16: LGTM!Standard Jest test configuration with appropriate compiler options for the Node test environment.
libs/blog/presentations/data-access/src/test-setup.ts (1)
1-9: LGTM!Excellent test setup with strict validation enabled. The
errorOnUnknownElementsanderrorOnUnknownPropertiesflags will catch template errors early, improving code quality.libs/blog/presentations/presentation-details-page/tsconfig.spec.json (1)
1-16: LGTM!Consistent test configuration matching other libraries in the workspace.
libs/blog/presentations/data-access/project.json (1)
1-20: LGTM!Well-configured data-access library with appropriate tags and targets.
libs/blog/presentations/presentations-page/tsconfig.json (1)
1-28: LGTM!Consistent TypeScript configuration with excellent strict settings and modern target, matching other libraries in the presentations feature.
libs/blog/shared/ui-ebook/tsconfig.json (1)
1-28: LGTM!Excellent consistency across the workspace - all libraries share the same strict TypeScript and Angular compiler settings, which promotes code quality and maintainability.
libs/blog-contracts/presentations/jest.config.ts (1)
1-11: LGTM!The Jest configuration is standard and correctly set up for the presentations library.
libs/blog/presentations/ui-presentation-card/src/lib/components/presentation-card/presentation-card.component.html (2)
1-11: LGTM!The card structure and accessibility setup is well-designed. The
aria-labelledbycorrectly references the title heading's id.
13-21: LGTM!The empty
alt=""attribute is appropriate here since the image is decorative and the link already has anaria-labelwith the presentation title. This prevents redundant announcements for screen reader users.libs/blog-contracts/presentations/src/lib/presentations.ts (1)
1-9: LGTM!The type definition appropriately extends
ArticlePreviewby removing article-specific fields and adding presentation-specific fields.libs/blog/presentations/presentation-details-page/.eslintrc.json (1)
1-36: LGTM!The ESLint configuration correctly enforces Angular naming conventions with the 'al' prefix and is consistent with other library configurations in the PR.
libs/blog/presentations/presentations-page/.eslintrc.json (1)
1-36: LGTM!The ESLint configuration is properly set up and consistent with the project's Angular coding standards.
libs/blog/presentations/presentation-details-page/src/lib/presentation-details-page/presentation-details-page.component.html (1)
30-30: LGTM!The ebook component integration is properly placed.
libs/blog/presentations/presentation-details-page/project.json (1)
1-20: LGTM!The NX project configuration is properly set up with appropriate test and lint targets, and the tags correctly identify this as a client-scoped feature library.
libs/blog/shared/ui-ebook/project.json (1)
1-20: LGTM!The NX project configuration is correctly set up for a UI component library with appropriate tags and targets.
libs/blog/presentations/ui-presentation-card/src/lib/components/presentation-card/presentation-card.stories.ts (1)
35-57: LGTM!The story definitions are well-structured with appropriate args and render templates. The fixed-width container ensures consistent preview sizing.
libs/blog/presentations/data-access/src/lib/state/presentation-detail.store.ts (2)
15-23: LGTM!The state type definition is clean and appropriately nullable for uninitialized data.
25-60: LGTM!The store implementation correctly uses NgRx Signals patterns with proper state management, loading states, and error handling. The use of
concatMapensures sequential processing of requests.libs/blog/presentations/data-access/src/lib/infrastructure/presentations.service.ts (1)
11-102: LGTM!The mock data is well-structured and consistent across all entries, providing good test data for the presentations feature.
libs/blog/presentations/presentations-page/src/lib/presentations-page/presentations-page.component.ts (2)
26-59: LGTM!The component follows established patterns from
CategoryArticlesComponentwith proper use of signals, computed values, and reactive store integration. The OnPush change detection strategy is appropriate for signal-based components.
61-64: LGTM!The
pageChangehandler correctly guards against SSR issues with the platform check and properly updates pagination state, which will reactively trigger a data refetch through the computed query.libs/blog/presentations/presentation-details-page/src/lib/presentation-details-page/presentation-details-page.component.ts (3)
1-14: LGTM!The imports are well-organized and appropriate for a signal-based Angular component with i18n support.
16-28: LGTM!The component configuration is correct with OnPush change detection, which is appropriate for signal-based components.
29-40: LGTM!The use of signal-based inputs and store integration follows modern Angular patterns correctly.
| getPresentationDetail(slug: string): Observable<PresentationPreview> { | ||
| // Find the presentation preview by slug | ||
| // This is a mock implementation; replace with real API call | ||
| const preview = MOCK_PRESENTATIONS.find((p) => p.slug === slug); | ||
|
|
||
| if (!preview) { | ||
| throw new Error(`Presentation with slug "${slug}" not found`); | ||
| } | ||
|
|
||
| return of(preview); | ||
| // TODO - add real API | ||
| // return this._http.get<PresentationDetails>( | ||
| // `${this._apiBaseUrl}/presentations/${slug}`, | ||
| // ); | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Use RxJS throwError for Observable error handling.
The synchronous throw statement breaks the Observable contract, making errors uncatchable by RxJS error handlers in the consuming store.
Apply this diff to fix the error handling:
-import { Observable, of } from 'rxjs';
+import { Observable, of, throwError } from 'rxjs'; getPresentationDetail(slug: string): Observable<PresentationPreview> {
// Find the presentation preview by slug
// This is a mock implementation; replace with real API call
const preview = MOCK_PRESENTATIONS.find((p) => p.slug === slug);
if (!preview) {
- throw new Error(`Presentation with slug "${slug}" not found`);
+ return throwError(() => new Error(`Presentation with slug "${slug}" not found`));
}
return of(preview);Committable suggestion skipped: line range outside the PR's diff.
🤖 Prompt for AI Agents
In
libs/blog/presentations/data-access/src/lib/infrastructure/presentations.service.ts
around lines 123 to 137, the method uses a synchronous throw which breaks the
Observable contract; change the code to return an RxJS error Observable instead:
replace the throw with return throwError(() => new Error(`Presentation with slug
"${slug}" not found`)); and ensure throwError is imported from 'rxjs' so
consumers can catch the error via RxJS operators/subscriptions.
| type PresentationsListState = { | ||
| presentations: PresentationPreview[] | null; | ||
| query: PresentationsQuery; | ||
| total: number; | ||
| }; | ||
|
|
||
| const initialState: PresentationsListState = { | ||
| presentations: null, | ||
| query: null, | ||
| total: 0, | ||
| }; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
Align state type with actual query shape and initial value.
PresentationsPage passes { take, skip }, but PresentationsQuery likely lacks skip. Also query is initialized to null. Keep query nullable in state but make the DTO non-null and include skip.
Apply in this file (state typing only):
type PresentationsListState = {
presentations: PresentationPreview[] | null;
- query: PresentationsQuery;
+ query: PresentationsQuery | null;
total: number;
};
const initialState: PresentationsListState = {
presentations: null,
query: null,
total: 0,
};And update the DTO (outside this file):
- File: libs/blog/presentations/data-access/src/lib/dto/presentations.query.ts
-export type PresentationsQuery = {
- take?: number;
-} | null;
+export type PresentationsQuery = {
+ take?: number;
+ skip?: number;
+};Verify occurrences:
🏁 Script executed:
#!/bin/bash
# Inspect PresentationsQuery definition and usages
rg -nC2 "export type PresentationsQuery" libs/blog/presentations/data-access/src/lib/dto/
rg -nC2 "\bPresentationsQuery\b"Length of output: 3443
I need to inspect more details about how the query is actually used. Let me examine the service implementation and how the query is consumed.
I need to examine the actual service implementation and how the query is being used:
Let me directly inspect the codebase to see how the query is being used:
🏁 Script executed:
#!/bin/bash
# Inspect getPresentationsList full implementation
rg -nA30 "getPresentationsList" libs/blog/presentations/data-access/src/lib/infrastructure/presentations.service.ts
# Also inspect fetchPresentationsList usage in the store
rg -nA20 "fetchPresentationsList" libs/blog/presentations/data-access/src/lib/state/presentations-list.store.tsLength of output: 241
---
🏁 Script executed:
```shell
#!/bin/bash
# Inspect getPresentationsList full implementation
rg -nA30 "getPresentationsList" libs/blog/presentations/data-access/src/lib/infrastructure/presentations.service.ts
# Also inspect fetchPresentationsList usage in the store
rg -nA20 "fetchPresentationsList" libs/blog/presentations/data-access/src/lib/state/presentations-list.store.ts
Length of output: 1987
Let me check where fetchPresentationsList is called and what parameters are passed:
---
🏁 Script executed:
```shell
#!/bin/bash
# Find where fetchPresentationsList is invoked
rg -nB3 -A3 "\.fetchPresentationsList\("
# Also search for PresentationsPage to see how it's calling the store
rg -nB5 -A5 "PresentationsPage" --type=ts --type=tsx
Length of output: 913
Let me examine the full context of the presentations-page component to see what fields are being passed:
---
🏁 Script executed:
```shell
#!/bin/bash
# Get full context around the fetchPresentationsList call
rg -nB20 "fetchPresentationsList" libs/blog/presentations/presentations-page/src/lib/presentations-page/presentations-page.component.ts | head -40
# Also look for pagination method definition
rg -nA10 "pagination\(" libs/blog/presentations/presentations-page/src/lib/presentations-page/presentations-page.component.ts
Length of output: 933
Apply both changes as specified in the review comment.
The verification confirms the issues identified:
-
DTO incomplete:
PresentationsPageComponentpasses bothtakeandskip(line 47:{ take: 12, skip: 0 }), butPresentationsQueryonly declarestake. Theskipfield is silently lost. -
Nullability misalignment: The DTO embeds nullability (
| null), but the store spreads the query at line 45 ({ ...query }) without null-checking. If query were null, this would break at runtime. -
Type safety: Moving nullability from DTO to state level (making DTO non-null and complete) properly separates concerns and aligns with actual usage.
Update both files as suggested:
- Add
skip?: numberto DTO and remove| null - Change state type to
query: PresentationsQuery | null
🤖 Prompt for AI Agents
In libs/blog/presentations/data-access/src/lib/state/presentations-list.store.ts
around lines 16-26, change the state type so query is nullable (query:
PresentationsQuery | null) instead of embedding nullability in the DTO; and in
libs/blog/presentations/data-access/src/lib/dto/presentations-query.dto.ts
(update the DTO file) remove the union with null and add an optional skip?:
number property to PresentationsQuery (so it includes take: number and skip?:
number and is non-nullable); ensure code that spreads or uses query checks for
null where appropriate after this change.
| <img | ||
| alt="" | ||
| class="m-auto mb-4 h-auto w-full object-cover md:w-1/3" | ||
| ngSrc="assets/article-placeholder.webp" | ||
| width="405" | ||
| height="250" | ||
| priority | ||
| /> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Use the presentation's featured image instead of a static placeholder.
The template always displays a static placeholder image. It should use presentation.featuredImageUrl with a fallback to the placeholder.
Apply this diff:
<img
alt=""
class="m-auto mb-4 h-auto w-full object-cover md:w-1/3"
- ngSrc="assets/article-placeholder.webp"
+ [ngSrc]="presentation.featuredImageUrl || 'assets/article-placeholder.webp'"
width="405"
height="250"
priority
/>📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| <img | |
| alt="" | |
| class="m-auto mb-4 h-auto w-full object-cover md:w-1/3" | |
| ngSrc="assets/article-placeholder.webp" | |
| width="405" | |
| height="250" | |
| priority | |
| /> | |
| <img | |
| alt="" | |
| class="m-auto mb-4 h-auto w-full object-cover md:w-1/3" | |
| [ngSrc]="presentation.featuredImageUrl || 'assets/article-placeholder.webp'" | |
| width="405" | |
| height="250" | |
| priority | |
| /> |
🤖 Prompt for AI Agents
In
libs/blog/presentations/presentation-details-page/src/lib/presentation-details-page/presentation-details-page.component.html
around lines 7 to 14, the template currently uses a static placeholder image;
change the img to bind ngSrc to the presentation's featured image with a
fallback to the placeholder (e.g. [ngSrc]="presentation.featuredImageUrl ||
'assets/article-placeholder.webp'"), keep width/height/priority attributes, and
ensure the alt attribute uses a meaningful value (e.g. presentation.title or a
default) so the component displays the actual featured image when available and
falls back to the placeholder.
| <a | ||
| al-button | ||
| role="link" | ||
| class="mx-auto mb-16 !block max-w-fit" | ||
| routerLink="#" | ||
| size="medium" | ||
| > | ||
| {{ 'Watch recording' }} | ||
| </a> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fix dead link and use translation for button text.
Two issues here:
- The
routerLink="#"is a dead link. Based on thePresentationPreviewtype, you should usepresentation.presentationUrl. - The button text is hardcoded instead of using the translation key.
Apply this diff:
<a
al-button
role="link"
class="mx-auto mb-16 !block max-w-fit"
- routerLink="#"
+ [href]="presentation.presentationUrl"
+ target="_blank"
+ rel="noopener noreferrer"
size="medium"
>
- {{ 'Watch recording' }}
+ {{ t('watchRecording') }}
</a>Note: Changed from routerLink to href since presentationUrl is likely an external URL (e.g., YouTube).
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| <a | |
| al-button | |
| role="link" | |
| class="mx-auto mb-16 !block max-w-fit" | |
| routerLink="#" | |
| size="medium" | |
| > | |
| {{ 'Watch recording' }} | |
| </a> | |
| <a | |
| al-button | |
| role="link" | |
| class="mx-auto mb-16 !block max-w-fit" | |
| [href]="presentation.presentationUrl" | |
| target="_blank" | |
| rel="noopener noreferrer" | |
| size="medium" | |
| > | |
| {{ t('watchRecording') }} | |
| </a> |
🤖 Prompt for AI Agents
In
libs/blog/presentations/presentation-details-page/src/lib/presentation-details-page/presentation-details-page.component.html
around lines 21 to 29, the anchor uses a dead internal routerLink="#" and a
hardcoded label; replace routerLink="#" with href bound to the presentation URL
(presentation.presentationUrl) since it is likely external, and replace the
hardcoded "Watch recording" text with the translation key (e.g., {{
'presentations.watchRecording' | translate }} or the project's i18n pipe) so the
button navigates to the actual recording and uses localized text.
| <div class="bg-al-footer-background mb-16 p-8 text-center"> | ||
| <h2 class="mb-4 text-2xl">{{ t('workshopsTitle') }}</h2> | ||
| <p class="mx-auto mb-8 max-w-prose"> | ||
| {{ t('workshopsDescription') }} | ||
| </p> | ||
| <a | ||
| al-button | ||
| role="link" | ||
| class="mx-auto !block max-w-fit" | ||
| routerLink="#" | ||
| size="medium" | ||
| > | ||
| {{ t('workshopsButton') }} | ||
| </a> | ||
| </div> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
Fix dead link in workshops section.
The workshops button has routerLink="#" which is a dead link. This should route to an actual workshops page or be updated with the correct route.
Please confirm the intended route for the workshops button and update accordingly.
---
🏁 Script executed:
```shell
#!/bin/bash
# Search for routing configuration and workshops-related routes
fd -e ts -e js -e json | xargs grep -l "workshops" | head -20
Length of output: 67
🏁 Script executed:
#!/bin/bash
# Check for RouterModule and route definitions
fd -e ts | xargs grep -l "Routes\|RouterModule" | head -10Length of output: 194
🏁 Script executed:
#!/bin/bash
# Look for other routerLink patterns with "#" to see if it's a common pattern
rg -n "routerLink\s*=\s*['\"]#['\"]" --type html --type ts -A2 -B2Length of output: 1990
🏁 Script executed:
#!/bin/bash
# Search for TODO or FIXME comments related to workshops or routes
rg -i "todo|fixme|placeholder" --type html --type ts | grep -i "workshop\|route\|link" | head -10Length of output: 0
🏁 Script executed:
#!/bin/bash
# Look at the file context to see if there are similar patterns in nearby components
fd "presentation-details-page.component" -e html -e ts | head -5Length of output: 247
---
🏁 Script executed:
```shell
#!/bin/bash
# Examine the main app routing configuration
cat apps/blog/src/app/app.config.ts | head -100
Length of output: 2299
🏁 Script executed:
#!/bin/bash
# Check the blog shell routes
cat libs/blog/shell/feature-shell-web/src/lib/blog-shell.routes.tsLength of output: 3094
🏁 Script executed:
#!/bin/bash
# Look for any workshops-related strings in route files to see if it's defined anywhere
rg "workshops" apps/blog/src/app/ libs/blog/shell/ --type tsLength of output: 0
🏁 Script executed:
#!/bin/bash
# Check the component TypeScript to see if there's any dynamic routing logic
cat libs/blog/presentations/presentation-details-page/src/lib/presentation-details-page/presentation-details-page.component.tsLength of output: 1487
Fix dead link in workshops section.
The workshops button at line 40 has routerLink="#" which is a dead link. The workshops route is not defined in the application routing configuration (verified in libs/blog/shell/feature-shell-web/src/lib/blog-shell.routes.ts). Either implement the workshops route or update the button to link to an existing feature.
| @Component({ | ||
| selector: 'al-presentation-card', | ||
| templateUrl: './presentation-card.component.html', | ||
| changeDetection: ChangeDetectionStrategy.OnPush, | ||
| imports: [ | ||
| AvatarComponent, | ||
| RouterLink, | ||
| NgOptimizedImage, | ||
| FastSvgComponent, | ||
| AlLocalizePipe, | ||
| ], | ||
| }) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Make the card component standalone.
Used via imports and lazy loadComponent; must declare standalone: true.
@Component({
selector: 'al-presentation-card',
+ standalone: true,
templateUrl: './presentation-card.component.html',
changeDetection: ChangeDetectionStrategy.OnPush,
imports: [📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| @Component({ | |
| selector: 'al-presentation-card', | |
| templateUrl: './presentation-card.component.html', | |
| changeDetection: ChangeDetectionStrategy.OnPush, | |
| imports: [ | |
| AvatarComponent, | |
| RouterLink, | |
| NgOptimizedImage, | |
| FastSvgComponent, | |
| AlLocalizePipe, | |
| ], | |
| }) | |
| @Component({ | |
| selector: 'al-presentation-card', | |
| standalone: true, | |
| templateUrl: './presentation-card.component.html', | |
| changeDetection: ChangeDetectionStrategy.OnPush, | |
| imports: [ | |
| AvatarComponent, | |
| RouterLink, | |
| NgOptimizedImage, | |
| FastSvgComponent, | |
| AlLocalizePipe, | |
| ], | |
| }) |
🤖 Prompt for AI Agents
In
libs/blog/presentations/ui-presentation-card/src/lib/components/presentation-card/presentation-card.component.ts
around lines 10-21, the @Component decorator is missing standalone: true; add
standalone: true to the decorator config so the component can be used via
imports and lazy loadComponent, keep the existing imports array unchanged, and
then remove or update any NgModule declarations or exports that previously
declared this component if they now import it directly as a standalone
component.
| const presentation: PresentationPreview = { | ||
| slug: 'lazy-but-fast-how-taking-it-slow-can-speed-up-your-app', | ||
| title: 'Lazy, but Fast: How Taking It Slow Can Speed Up Your App!', | ||
| excerpt: | ||
| 'Learn how lazy loading can actually improve your application performance.', | ||
| featuredImageUrl: 'https://via.placeholder.com/405x228', | ||
| publishDate: '2025-03-15', | ||
| author: { | ||
| name: 'Jarosław Żołnowski', | ||
| slug: 'jaroslaw-zolnowski', | ||
| avatarUrl: 'https://via.placeholder.com/150', | ||
| }, | ||
| }; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add missing required properties.
The presentation mock is missing eventName and presentationUrl properties required by the PresentationPreview type.
Apply this diff to add the missing properties:
const presentation: PresentationPreview = {
slug: 'lazy-but-fast-how-taking-it-slow-can-speed-up-your-app',
title: 'Lazy, but Fast: How Taking It Slow Can Speed Up Your App!',
excerpt:
'Learn how lazy loading can actually improve your application performance.',
featuredImageUrl: 'https://via.placeholder.com/405x228',
publishDate: '2025-03-15',
+ eventName: 'Angular Camp',
+ presentationUrl: 'https://www.youtube.com/@angularlove/videos',
author: {
name: 'Jarosław Żołnowski',
slug: 'jaroslaw-zolnowski',
avatarUrl: 'https://via.placeholder.com/150',
},
};📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| const presentation: PresentationPreview = { | |
| slug: 'lazy-but-fast-how-taking-it-slow-can-speed-up-your-app', | |
| title: 'Lazy, but Fast: How Taking It Slow Can Speed Up Your App!', | |
| excerpt: | |
| 'Learn how lazy loading can actually improve your application performance.', | |
| featuredImageUrl: 'https://via.placeholder.com/405x228', | |
| publishDate: '2025-03-15', | |
| author: { | |
| name: 'Jarosław Żołnowski', | |
| slug: 'jaroslaw-zolnowski', | |
| avatarUrl: 'https://via.placeholder.com/150', | |
| }, | |
| }; | |
| const presentation: PresentationPreview = { | |
| slug: 'lazy-but-fast-how-taking-it-slow-can-speed-up-your-app', | |
| title: 'Lazy, but Fast: How Taking It Slow Can Speed Up Your App!', | |
| excerpt: | |
| 'Learn how lazy loading can actually improve your application performance.', | |
| featuredImageUrl: 'https://via.placeholder.com/405x228', | |
| publishDate: '2025-03-15', | |
| eventName: 'Angular Camp', | |
| presentationUrl: 'https://www.youtube.com/@angularlove/videos', | |
| author: { | |
| name: 'Jarosław Żołnowski', | |
| slug: 'jaroslaw-zolnowski', | |
| avatarUrl: 'https://via.placeholder.com/150', | |
| }, | |
| }; |
🤖 Prompt for AI Agents
In
libs/blog/presentations/ui-presentation-card/src/lib/components/presentation-card/presentation-card.stories.ts
around lines 8 to 20, the presentation mock is missing the required
PresentationPreview properties eventName and presentationUrl; update the
presentation object to include an eventName (string) and presentationUrl
(string) fields with appropriate mock values (e.g., event title and a URL) so
the object matches the PresentationPreview type.
| <a | ||
| al-button | ||
| role="link" | ||
| class="justify-self-center md:justify-self-start" | ||
| routerLink="#" | ||
| size="medium" | ||
| > | ||
| {{ t('downloadButton') }} | ||
| </a> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Replace placeholder link with functional download URL.
The routerLink="#" is a non-functional placeholder. Since this is an ebook download button, it should either:
- Link to the actual ebook file/URL
- Trigger a download action via click handler
- Navigate to a proper route
Consider one of these approaches:
Option 1: Direct file download
<a
al-button
role="link"
class="justify-self-center md:justify-self-start"
- routerLink="#"
+ href="/assets/ebooks/angular-ebook.pdf"
+ download
size="medium"
>Option 2: External link
<a
al-button
role="link"
class="justify-self-center md:justify-self-start"
- routerLink="#"
+ href="https://example.com/ebook"
+ target="_blank"
+ rel="noopener noreferrer"
size="medium"
>Option 3: Component method
- <a
+ <button
al-button
- role="link"
+ type="button"
class="justify-self-center md:justify-self-start"
- routerLink="#"
+ (click)="downloadEbook()"
size="medium"
>
{{ t('downloadButton') }}
- </a>
+ </button>🤖 Prompt for AI Agents
In libs/blog/shared/ui-ebook/src/lib/ui-ebook/ui-ebook.component.html around
lines 12 to 20, the anchor uses routerLink="#" which is a non-functional
placeholder; replace it with a real download behavior by one of three
approaches: (1) direct file download — change to a normal anchor with href bound
to the ebook file/asset URL and include the download attribute so the browser
saves it, ensuring the component provides the correct URL; (2) external link —
use target="_blank" rel="noopener" and href bound to the external ebook URL if
hosted elsewhere; or (3) component method — convert to a button or keep as
anchor but add a click handler that calls a component method which retrieves the
file (or generates a blob) and triggers programmatic download or navigates to
the proper route; ensure ARIA/role and styles are preserved and that the
component exposes the ebook URL or download method via an @Input or service.
| @Component({ | ||
| selector: 'al-ebook', | ||
| imports: [ | ||
| CardComponent, | ||
| GradientCardDirective, | ||
| ButtonComponent, | ||
| NgOptimizedImage, | ||
| TranslocoDirective, | ||
| ], | ||
| templateUrl: './ui-ebook.component.html', | ||
| styleUrl: './ui-ebook.component.scss', | ||
| changeDetection: ChangeDetectionStrategy.OnPush, | ||
| }) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Component must be standalone.
This is imported by pages/components; add standalone: true.
@Component({
selector: 'al-ebook',
+ standalone: true,
imports: [
CardComponent,
GradientCardDirective,
ButtonComponent,
NgOptimizedImage,
TranslocoDirective,
],📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| @Component({ | |
| selector: 'al-ebook', | |
| imports: [ | |
| CardComponent, | |
| GradientCardDirective, | |
| ButtonComponent, | |
| NgOptimizedImage, | |
| TranslocoDirective, | |
| ], | |
| templateUrl: './ui-ebook.component.html', | |
| styleUrl: './ui-ebook.component.scss', | |
| changeDetection: ChangeDetectionStrategy.OnPush, | |
| }) | |
| @Component({ | |
| selector: 'al-ebook', | |
| standalone: true, | |
| imports: [ | |
| CardComponent, | |
| GradientCardDirective, | |
| ButtonComponent, | |
| NgOptimizedImage, | |
| TranslocoDirective, | |
| ], | |
| templateUrl: './ui-ebook.component.html', | |
| styleUrl: './ui-ebook.component.scss', | |
| changeDetection: ChangeDetectionStrategy.OnPush, | |
| }) |
🤖 Prompt for AI Agents
In libs/blog/shared/ui-ebook/src/lib/ui-ebook/ui-ebook.component.ts around lines
11 to 23, the Angular component is missing the standalone flag required for
direct importing into pages/components; add standalone: true to the @Component
decorator metadata alongside the existing imports array so the component becomes
a standalone component and can be imported without declaring it in an NgModule.
| "@angular-love/blog-contracts/presentations": [ | ||
| "libs/blog-contracts/presentations/src/index.ts" | ||
| ], |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
Verify duplicate path mappings for presentations contracts.
Two different aliases map to the same physical path:
@angular-love/blog-contracts/presentations(lines 39-41)@angular-love/blog/contracts/presentations(lines 100-102)
Both point to libs/blog-contracts/presentations/src/index.ts. If this is intentional for backward compatibility or to support multiple import styles, consider adding a comment. Otherwise, standardize on one alias.
Run this script to check which aliases are actually used in the codebase:
Also applies to: 100-102
🏁 Script executed:
#!/bin/bash
# Check usage of both presentation contract aliases
echo "=== Checking @angular-love/blog-contracts/presentations usage ==="
rg -n --type=ts -C2 "from ['\"]@angular-love/blog-contracts/presentations" -g '!tsconfig.base.json'
echo -e "\n=== Checking @angular-love/blog/contracts/presentations usage ==="
rg -n --type=ts -C2 "from ['\"]@angular-love/blog/contracts/presentations" -g '!tsconfig.base.json'Length of output: 3706
Remove the unused presentation contracts alias at lines 100-102.
Verification shows @angular-love/blog-contracts/presentations is actively imported in 5 files across the codebase, while @angular-love/blog/contracts/presentations has zero usage. The alias at lines 100-102 is dead code and should be removed to clean up the tsconfig configuration.
🤖 Prompt for AI Agents
tsconfig.base.json around lines 39 to 41: remove the unused path mapping entry
for "@angular-love/blog-contracts/presentations" (the block mapping to
"libs/blog-contracts/presentations/src/index.ts") from the "paths" section;
after removal, save the file and run a quick project-wide search/build to ensure
no imports break and commit the cleaned-up tsconfig change.
|
PR is detected, will deploy to dev environment |
|
Deployed to dev environment |
Summary by CodeRabbit
New Features
Bug Fixes
Documentation